﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using XFABManager;

namespace XFGameFramework
{

    public interface IReuseable
    {
        void OnLoaded();
        void OnUnloaded();
    }

    /// <summary>
    /// 类的对象池
    /// </summary>
    public class ClassObjectPoolManager
    {

        #region 常量

        private const int UNLOAD_OUT_TIME = 60 * 10; // 10 分钟

        private const int DETECTION_INTERVAL = 60;
        #endregion

        #region 字段

        // Fix编码
        private static Dictionary<Type, List<IReuseable>> objects = new Dictionary<Type, List<IReuseable>>();

        private static Dictionary<IReuseable, float> unloaded_times = new Dictionary<IReuseable, float>();

        private static List<Type> temp_keys = new List<Type>();
        #endregion

        #region 方法

        [RuntimeInitializeOnLoadMethod()]
        static void InitClassObjectPoolManager()
        {

            objects.Clear();
            unloaded_times.Clear();
            temp_keys.Clear();
             
            CoroutineStarter.Start(Check());
        }




        /// <summary>
        /// 加载对象
        /// 根据类型去对象池查找是否有空闲的对象，如果有则返回该空闲对象，如果没有空闲对象，则创建对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T Load<T>() where T : class, IReuseable
        {
            // 判断字典中是否有闲置的 
            Type type = typeof(T);

            // 查询对象池中是否有数据
            if (objects.ContainsKey(type) && objects[type].Count > 0)
            {
                IReuseable obj = objects[type].Last();
                objects[type].Remove(obj);
                if (unloaded_times.ContainsKey(obj))
                    unloaded_times.Remove(obj);
                if (objects[type].Count == 0)
                    objects.Remove(type);

                obj.OnLoaded();
                return obj as T;
            }

            // 没有查询到数据 直接return!
            T t = Activator.CreateInstance<T>();
            t.OnLoaded();
            return t;
        }

        /// <summary>
        /// 加载对象
        /// 根据类型去对象池查找是否有空闲的对象，如果有则返回该空闲对象，如果没有空闲对象，则创建对象
        /// </summary> 
        /// <returns></returns>
        public static IReuseable Load(Type type)
        {

            if (!typeof(IReuseable).IsAssignableFrom(type))
                throw new Exception("未实现IReuseable接口!");


            // 查询对象池中是否有数据
            if (objects.ContainsKey(type) && objects[type].Count > 0)
            {
                IReuseable obj = objects[type].Last();
                objects[type].Remove(obj);
                if (unloaded_times.ContainsKey(obj))
                    unloaded_times.Remove(obj);
                if (objects[type].Count == 0)
                    objects.Remove(type);

                SetDefaultValues(obj);
                obj.OnLoaded();
                return obj;
            }


            IReuseable t = null;

            try
            {
                // 没有查询到数据 直接return!
                t = Activator.CreateInstance(type) as IReuseable;
                SetDefaultValues(t);
                t.OnLoaded();
            }
            catch (MissingMethodException)
            {
                throw new Exception(string.Format("类型:{0}请提供无参构造函数!", type.FullName));
            }


            return t;
        }

        /// <summary>
        /// 回收对象
        /// 把对象放到对象池中,等待下次使用,如果超过10分钟仍未被使用,会被自动清理!
        /// </summary>
        /// <param name="reuseable"></param>
        public static void Unload(IReuseable reuseable)
        {
            // 放回到对象池中 
            if (reuseable == null)
                return;

            // 已经回收了
            if (unloaded_times.ContainsKey(reuseable))
                return;

            Type type = reuseable.GetType();
            unloaded_times.Add(reuseable, Time.time);
            reuseable.OnUnloaded();
            if (objects.ContainsKey(type))
                objects[type].Add(reuseable);
            else
                objects.Add(type, new List<IReuseable>() { reuseable });
        }

        // 如果保存的对象超过一定的时间 就把它移除
        private static IEnumerator Check()
        {
            while (true)
            {
                // 1分钟检测一次
                yield return new WaitForSeconds(DETECTION_INTERVAL);

                temp_keys.Clear();

                foreach (var key in objects.Keys)
                {
                    List<IReuseable> item = objects[key];

                    for (var i = 0; i < item.Count; i++)
                    {
                        if (Time.time - unloaded_times[item[i]] >= UNLOAD_OUT_TIME)
                        {
                            unloaded_times.Remove(item[i]);
                            item.RemoveAt(i);
                            i--;
                        }
                    }

                    if (item.Count == 0)
                    {
                        temp_keys.Add(key);
                    }
                }

                foreach (var key in temp_keys)
                {
                    objects.Remove(key);
                }
            }
        }


        public static void SetDefaultValues(object obj)
        {
            if (obj == null) return;

            Type type = obj.GetType();

            // 设置字段的默认值
            foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                if (!field.IsInitOnly) // 忽略只读字段 
                    field.SetValue(obj, GetDefaultValue(field.FieldType));
            }

            // 设置属性的默认值
            foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                if (property.CanWrite)
                    property.SetValue(obj, GetDefaultValue(property.PropertyType), null);
            }
        }

        private static object GetDefaultValue(Type type)
        {
            if (type.IsValueType)
            {
                return Activator.CreateInstance(type);
            }
            else
            {
                return null;
            }
        }

        #endregion
    }
}


